Gradient fade effect
Before we put away
our headline rotator example, let's give it a finishing touch, by
making the headline text appear to fade in from the bottom of its
container. The effect will be a gradient fade, appearing as if the text is opaque at the top of the effect area and transparent at the bottom.
A single text element cannot
have multiple opacities simultaneously, however. To simulate this,
we'll actually cover up the effect area with a series of elements, each
of which has a different opacity. These slices with be<div> elements with a few style properties in common, which we can declare in our stylesheet:
.fade-slice {
position: absolute;
width: 20em;
height: 2px;
background: #efd;
z-index: 3;
}
They all have the same width and background-color properties as their containing element,<div id="news-feed">. This will fool the user's eye into thinking the text is fading away, rather than being covered up by another element.
Now we can create the<div class="fade-slice">
elements. To make sure we have the right number of them, first we'll
determine a height in pixels for the entire effect area. In this case,
we're choosing 25 percent of the<div id="news-feed"> height. We'll use a for loop to iterate across the height of this area, creating a new slice element for each 2-pixel segment of the gradient:
$(document).ready(function() {
$('#news-feed').each(function() {
var $container = $(this);
$container.empty();
var fadeHeight = $container.height() / 4;
for (var yPos = 0; yPos < fadeHeight; yPos += 2) {
$('<div></div>')
.addClass('fade-slice')
.appendTo($container);
}
});
});
Now we have 25 slices (one
for each 2-pixel segment of the 50-pixel gradient area), but they are
all piled up at the top of the container. For our trick to work, we
need each one to have a different position and opacity. We can use the
iteration variable yPos to mathematically determine each element's opacity and top properties:
$(document).ready(function() {
$('#news-feed').each(function() {
var $container = $(this);
$container.empty();
var fadeHeight = $container.height() / 4;
for (var yPos = 0; yPos < fadeHeight; yPos += 2) {
$('<div></div>').css({
opacity: yPos / fadeHeight,
top: $container.height() - fadeHeight + yPos
}).addClass('fade-slice').appendTo($container);
}
});
});
These calculations can be a bit tricky to visualize, so we'll lay out
the numbers in a table. The opacity values step up incrementally from
transparent to opaque, as the top values begin at the top of the fade
area (150) and grow to the container's height:
yPos
|
opacity
|
top
|
---|
0
|
0
|
150
|
2
|
0.04
|
152
|
4
|
0.08
|
154
|
6
|
0.12
|
156
|
8
|
0.16
|
158
|
|
…
| |
40
|
0.80
|
190
|
42
|
0.84
|
192
|
44
|
0.88
|
194
|
46
|
0.92
|
196
|
48
|
0.96
|
198
|
Keep in mind that since the top position of the final<div class="fade-slice"> is 198, its 2-pixel height will neatly overlay the bottom two pixels of the 200-pixel-tall containing<div>.
With our code in place, the
text in the headline area of the page now blends beautifully from
transparent to opaque as it overlaps the bottom of the container:
The finished code
Our first rotator is now
complete. The news items are now fetched from a remote server,
formatted, animated in and out of view on schedule, and beautifully
styled:
$(document).ready(function() {
$('#news-feed').each(function() {
var $container = $(this);
$container.empty();
var fadeHeight = $container.height() / 4;
for (var yPos = 0; yPos < fadeHeight; yPos += 2) {
$('<div></div>').css({
opacity: yPos / fadeHeight,
top: $container.height() - fadeHeight + yPos
}).addClass('fade-slice').appendTo($container);
}
var $loadingIndicator = $('<img/>')
.attr({
'src': 'images/loading.gif',
'alt': 'Loading. Please wait.'
})
.addClass('news-wait')
.appendTo($container);
$.get('news/feed.php', function(data) {
$loadingIndicator.remove();
$('rss item', data).each(function() {
var $link = $('<a></a>')
.attr('href', $('link', this).text())
.text($('title', this).text());
var $headline = $('<h4></h4>').append($link);
var pubDate = new Date($('pubDate', this).text());
var pubMonth = pubDate.getMonth() + 1;
var pubDay = pubDate.getDate();
var pubYear = pubDate.getFullYear();
var $publication = $('<div></div>')
.addClass('publication-date')
.text(pubMonth + '/' + pubDay + '/' + pubYear);
var $summary = $('<div></div>')
.addClass('summary')
.html($('description', this).text());
$('<div></div>')
.addClass('headline')
headline rotatorcode.append($headline, $publication, $summary)
.appendTo($container);
});
var currentHeadline = 0, oldHeadline = 0;
var hiddenPosition = $container.height() + 10;
$('div.headline').eq(currentHeadline).css('top', 0);
var headlineCount = $('div.headline').length;
var pause;
var rotateInProgress = false;
var headlineRotate = function() {
if (!rotateInProgress) {
rotateInProgress = true;
pause = false;
currentHeadline = (oldHeadline + 1)
% headlineCount;
$('div.headline').eq(oldHeadline).animate(
{top: -hiddenPosition}, 'slow', function() {
$(this).css('top', hiddenPosition);
});
$('div.headline').eq(currentHeadline).animate(
{top: 0}, 'slow', function() {
rotateInProgress = false;
if (!pause) {
pause = setTimeout(headlineRotate, 5000);
}
});
oldHeadline = currentHeadline;
}
};
if (!pause) {
pause = setTimeout(headlineRotate, 5000);
headline rotatorcode}
$container.hover(function() {
clearTimeout(pause);
pause = false;
}, function() {
if (!pause) {
pause = setTimeout(headlineRotate, 250);
}
});
});
});
});